package haxeframe.physics;

import box2D.dynamics.B2Body;
import box2D.dynamics.B2BodyDef;
import box2D.dynamics.B2World;
import box2D.dynamics.B2FilterData;
import box2D.dynamics.B2Fixture;

import haxe.xml.Fast;

import haxeframe.Globals;
import haxeframe.loading.LoadManager;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;

import haxeframe.physics.Assets;
import haxeframe.physics.fixtures.Fixture;
import haxeframe.physics.fixtures.FixtureBox;
import haxeframe.physics.fixtures.FixtureCircle;
import haxeframe.physics.fixtures.FixturePolygon;
import openfl.display.Tilesheet;

class Symbol extends Sprite
{
	public static var BOX:String = 'box';
	public static var CIRCLE:String = 'circle';
	public static var POLYGON:String = 'polygon';

	public var world:B2World;
	public var _body:B2Body;
	public var _scale:Float = 30;
	public var _config:Xml;
	private var _bodyType:Int = 0;
	
	public var _fixtures:Array<Fixture>;
	
	public var remove:Bool;
	
	public var formerParent:DisplayObjectContainer;
	
	public var _updating:Bool;

	private var _maskBits:Int = 0xFFFF;
	private var _categoryBits:Int = 0x0001;

	public var symbolName:String;

	private var _x:Float = 0;
	private var _y:Float = 0;

	public var addToBack:Bool = false;

	public var tilesheet:Tilesheet;
	public var tilesheetXml:Xml;

	// public var tiles:Array<String>;
	
	public function new(config:Xml, _world:B2World, scale:Float)
	{
 		super ();
		world = _world;
		_scale = scale;
		_config = config;
		if(_config.exists('name')) symbolName = _config.get('name');

		// tiles = [];

		// for(a in _config.elementsNamed('asset'))
		// {
		// 	if(a.exists('tile')) {
		// 		var nameParts:Array<String> = a.get('tile').split("/");
		// 		nameParts = nameParts[nameParts.length - 1].split(".");
		// 		tiles.push(nameParts[0]);
		// 	}
		// }

		addEventListener(Event.ADDED_TO_STAGE, onAdded);
		addEventListener(Event.REMOVED_FROM_STAGE, onRemoved);
	}

	public static function Symbol()
	{	
	}

	public function setPosition(xPos:Float, yPos:Float):Void
	{
		_x = xPos;
		x = xPos*Globals.SCALE;
		_y = yPos;
		y = yPos*Globals.SCALE;
	}

	public function getPosition():Dynamic
	{
		return {x:_x, y:_y};
	}

	public function init():Void
	{
		reset();
	}
	
	public var maskBits(get,set):Int;
	public function get_maskBits():Int
	{
		return _maskBits;
	}

	public function set_maskBits(value:Int):Int
	{
		_maskBits = value;
		updateFilters();
		return _maskBits;
	}
	
	public var categoryBits(get,set):Int;
	public function get_categoryBits():Int
	{
		return _categoryBits;
	}

	public function set_categoryBits(value:Int):Int
	{
		_categoryBits = value;
		updateFilters();
		return _categoryBits;
	}

	private function updateFilters():Void
	{
		if(body == null) return;
		var f:B2Fixture = body.getFixtureList();
		while(f != null)
		{
			var filter:B2FilterData = f.getFilterData();
			filter.maskBits = maskBits;
			filter.categoryBits = categoryBits;
			f.setFilterData(filter);
			f = f.getNext();
		}
	}

	public var bodyType(get_bodyType, set_bodyType) : Int;

	public function get_bodyType():Int
	{
		return _bodyType;
	}

	public function set_bodyType(value:Int):Int
	{
		_bodyType = value;
		if(_body != null) _body.setType(_bodyType);
		return _bodyType;
	}
	
	public var updating(get_updating, set_updating) : Bool;

	public function get_updating():Bool

	{
		return _updating;
	}

	public function set_updating(value:Bool):Bool
	{
		_updating = value;
		return _updating;
	}
	
	public function destroyDisplay():Void
	{
		while(numChildren > 0){
			var image:Dynamic = getChildAt(0);
			removeChild(image);
		}
		graphics.clear();
	}
	
	public function createDisplay():Void
	{
		for(a in _config.elementsNamed('asset'))
		{
			if(a.exists('file')){
				
				var image:Bitmap = LoadManager.getBitmap(a.get('file'));
				if(image == null){
					trace('Error : could not get bitmap '+a.get('file'));
					 continue;
					}
				image.scaleX = Std.parseFloat(a.get('scaleX'))*Globals.SCALE_FACTOR;
				image.scaleY = Std.parseFloat(a.get('scaleY'))*Globals.SCALE_FACTOR;
				image.x = Std.parseFloat(a.get('x'))*Globals.SCALE;
				image.y = Std.parseFloat(a.get('y'))*Globals.SCALE;
				image.x += image.width * -0.5;
				image.y += image.height * -0.5;
				image.rotation = Std.parseFloat(a.get('rotation'));
				addChild(image);
			} else if(a.exists('tile')) {
				var nameParts:Array<String> = a.get('tile').split("/");
				nameParts = nameParts[nameParts.length - 1].split(".");
				// Assets.drawTile(tileName[0], this, true);
				var tileName:String = nameParts[0];
				var image:Bitmap = Assets.getTileAsBitmap(tileName);
				if(image == null){
					trace('Error : could not get bitmap '+a.get('tile'));
					 continue;
					}
				image.scaleX = Std.parseFloat(a.get('scaleX'))*Globals.SCALE_FACTOR;
				image.scaleY = Std.parseFloat(a.get('scaleY'))*Globals.SCALE_FACTOR;
				image.x = Std.parseFloat(a.get('x'))*Globals.SCALE;
				image.y = Std.parseFloat(a.get('y'))*Globals.SCALE;
				image.x += image.width * -0.5;
				image.y += image.height * -0.5;
				image.rotation = Std.parseFloat(a.get('rotation'));
				addChild(image);
			}
		}

		for(i in _config.elementsNamed('instance'))
		{
			var instance:Symbol = Assets.getSymbol(i.get('symbol'));
			instance.setPosition(Std.parseFloat(i.get('x')), Std.parseFloat(i.get('y')));
			instance.scaleX = Std.parseFloat(i.get('scaleX'));
			instance.scaleY = Std.parseFloat(i.get('scaleY'));
			instance.rotation = Std.parseFloat(i.get('rotation'));
			instance.world = world;
			instance.init();
			addChild(instance);
		}
		// flatten();
	}
	
	public function createBody():Void
	{
		if(!_config.elementsNamed('body').hasNext())
			return;
			
		var shapes:Iterator<Xml> = _config.elementsNamed('body').next().elements();
		
		var bodyDef:B2BodyDef = new B2BodyDef();
		bodyDef.position.x = (x + Std.parseFloat(_config.get('x'))) / _scale;
		bodyDef.position.y = (y + Std.parseFloat(_config.get('y'))) / _scale;
		bodyDef.userData = this;
		if(_config.get('allowSleep') == 'false') bodyDef.allowSleep = false;
		if(_config.exists('bodyType')) bodyType = Std.parseInt(_config.get('bodyType'));
		bodyDef.type = bodyType;
		_body = world.createBody(bodyDef);
		_body.setAngle((rotation + Std.parseFloat(_config.get('rotation'))));

		for(shape in shapes){

			switch(shape.nodeName){
				
				case 'box' :
					_fixtures.push(new FixtureBox(shape, _scale, scaleX));
				
				case 'circle' :
					_fixtures.push(new FixtureCircle(shape, _scale, scaleX));
				
				// case 'polygon' :
				// 	_fixtures.push(new FixturePolygon(shape, _scale, scaleX));
				
			}
			
		}

		for(f in _fixtures){
			for(d in f.definitions){
				_body.createFixture(d);
			}
		}
		_body.setUserData(this);
	}
	
	public function destroyBody():Void
	{
		if(_body != null) world.destroyBody(_body);
		_body = null;
	}
	
	public function reset():Void
	{
		_fixtures = new Array<Fixture>();
		
		destroyDisplay();
		destroyBody();
		
		if(formerParent != null) formerParent.addChild(this);
		
		createBody();
		createDisplay();
	}
	
	public function update():Void
	{

	}

	public var body(get_body, never) : B2Body;

	public function get_body():B2Body

	{
		return _body;
	}
	
	private function onAdded(event:Event):Void
	{
		formerParent = null;
		remove = false;
		
	}
	
	private function onRemoved(event:Event):Void
	{
	}
	
	public function removeFromParent(d:Bool=false):Void
	{
		if(this.parent != null){
			formerParent = this.parent;
			this.parent.removeChild(this);
		}
		if(d){
			destroyDisplay();
			destroyBody();
		}
		remove = false;
		// super.removeFromParent(d);
	}
	
	public function dispose():Void
	{
		destroyDisplay();
		destroyBody();
		Assets.returnSymbol(_config.get('name'), this);
		// super.dispose();
	}
	
}